//----BEGIN LICENSE----//
/*****************************************************************\
** prevalentLite - sqlite3 persistence object library 
**
** Copyright (C) 2008 Philip R. Schuchardt
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
*****************************************************************/
//----END LICENSE----//

/*!
\class plObject plObject.h <plObject>

\brief Is the base class for any object that wants their data stored into the database.

By inheriting this class and following the steps below, your objects will automatically be saved and loaded (persistent).  Objects are automatrically added to the database when ever there default constructor is called. Objects are automatrically remove from memory and the database when the deconstruct is called.  If you want an object to be removed from memory but not the database, call DeleteMemory().  Use the plDatabase::ReadOnly() context to avoid having the object's data saved or updated. Data is automatically commited to the database when it's value changes. There is significant IO overhead when plObject 's member data are changed or when plObject 's get delete'ed or new'ed.  To avoid this overhead put large blocks of code that update values between plDatabase::Begin() and plDatabase::End(). Once plDatabase::End() is called all the updates will be commited to the database.

Persistent objects are recoverable when the computer crashes, program crashes, or even in the event of power failure.  The database will automatrically be rolled back to the last commit.  If the crash happens between a plDatabase::Begin() and plDatabase::End(), none of that data will be recovered.  The system will not be left in "middle of the road" state.  Try to aviod updating data outside of Begin() and End() blocks since object maybe left in a bad state in event of a crash.

Steps to make a custom persistent object:
\li Inherit this class
\li The subclass must be regestered with the plFactory. This is easily done with pl_DECLARE_CLASS and pl_DEFINE_CLASS
\li Implement a default constructor that calls CreateObject()
\li Call plData::Setup() for all member data
\li Implement a destructor that calls DeleteObject()

Example Custom plObject (With inheritance):
\code

// ----------------- Person Class --------------------- //

class Person : public plObject

//Help add Person into the object Factory
pl_DEFINE_CLASS

public:
    //Constructor
    Person() {
        //Always call CreateObject() at the beginning of the constructor
        CreateObject();

        //Setup the member data
        Name.Setup("Name", this);
        Weight.Setup("Weight", this);
        Kid.Setup("Kid", this);

        //Start using member varibles
        Name = "John"
        Weight = 150; //150 pounds
        Kid = NULL;

        //This should work just fine
        Kid = new Kid();
    }

    //Destructor
    virtual ~Person() {
        delete Kid;

        //Always call DeleteObject() at the end of the constructor
        DeleteObject();
    }

    void SetName(const std::string& newName) -
        //Updates the database with the newName
        //YES! It's that simple!!!
        Name = newName;
    }

    const std::string& GetName() {
        //Gets the name out of the database
        return Name;
    }

    void SetWeight(double newWeight) {
        //Updates the database with the newWeight
        Weight = newWeight;
    }

    double GetWeight() {
        return Weight;
    }

    //Makes the this person have a Kid unless it already has a child
    void BirthChild() {
        if(Child != NULL) {
            Child = new Kid();
        }
    }


private:
    plString Name; //Name of the person
    plDouble Weight;  //Weight in lb
    plPointer<Kid> Child; //The child of the person
};

//Adds the person to the plFactory
pl_DEFINE_CLASS(Person)

// ------------------ Kid Class --------------------- //

class Kid : public Person {

pl_DECLARE_CLASS

public:
    //Constructor
    Kid() {
        CreateObject();

        Annoying.Setup("Annoying", this);

        Annoying = true; //Digital Kids are always annoying
    }

    //Deconstructor
    virtual ~Kid() {
        DeleteObject();
    }

    bool IsAnnoying() {
        return Annoying;
    }

private:
    plBool Annoying; //True if this kid is annoying
};

pl_DEFINE_CLASS(Kid)

\endcode

*/

//Debug includes
#include <iostream>

//Our includes
#include "plObject.h"
#include "plDatabase.h"
#include "plData.h"


///////////////////////////////////////// Static data ///////////////////////////////////////////
const int plObject::BadId = -1;

////////////////////////////////// Public //////////////////////////////////////

//! \brief Constructor
plObject::plObject() {
	IndexedColumns = NULL;
	SetId(BadId); //Invalid id
} //end plObject

/*!
    \brief  Deconstructor

Will remove the object from the database's memory

  */
plObject::~plObject() {
        plDatabase::GetDatabase()->RemoveFromMemory(this);

        if(IndexedColumns) {
            delete IndexedColumns;
        }
}

/*!
\brief Removes this object memory only

This function will not remove the object from the database's memory.  This function is useful if the object is no long need in memory.
*/
void plObject::DeleteMemory() {
        plDatabase::GetDatabase()->ReadOnly(true);
	delete this;
        plDatabase::GetDatabase()->ReadOnly(false);
} //end Delete()

/*!
    \brief Gets the database id for this object

    THIS IS A SYSTEM FUNCTION. YOU SHOULD NOT NEED TO USE THIS FUNCTION.

    \return The object's database memory address/id
  */
sqlite_int64 plObject::Id() const {
	return TableId;
} //end Id()

/*!
  \brief Sets the database memory address/id for the object

  THIS IS A SYSTEM FUNCTION. YOU SHOULD NOT NEED TO USE THIS FUNCTION.

  \param id The new id for the object
 */
void plObject::SetId(sqlite_int64 id) {
	TableId = id;
} //end SetId(int id)

/*!
\brief Indexes the a column for fast searching

NOT IMPLEMENTED
*/
void plObject::Index(plData* column) {
	//If indexedCulomns is null
	if(!IndexedColumns) {
                IndexedColumns = new std::list<plData*>();
	}
	
	//Add the column to the list
	IndexedColumns->push_back(column);
}

/*!

\brief Get's the database that the object is loading and saving from

\return The databases

 */
//Gets the database for all the objects
plDatabase* plObject::GetDatabase() {
        return plDatabase::GetDatabase();
}

////////////////////////////////////// Protected ///////////////////////////////////////

/*!
    \brief Creates the object in the database

    This should be the first line of code in your subclass's constructor
*/
void plObject::CreateObject() {
        plDatabase::GetDatabase()->AddObject(this);
}

/*!
    \brief Deletes the object out of the database

    This should be the last line of code in your subclass's destructor
*/
void plObject::DeleteObject() {
        plDatabase::GetDatabase()->DeleteObject(this);
}


